﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text;
using System.Transactions;
using Xyz.BusinessObjects;
using Xyz.DataObjects;
using Xyz.DataObjects.Interfaces;
using Xyz.Service.DataTransferObjects;
using Xyz.Service.ServiceContracts;

namespace Xyz.Service
{
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class BookingService : IBookingService
    {
        private static readonly IBookingDao BookingDao = DataAccess.BookingDao;
        private static readonly ITicketDao TicketDao = DataAccess.TicketDao;
        private static readonly IMemberPaymentDao MemberPaymentDao = DataAccess.MemberPaymentDao;
        private static readonly ICardPaymentDao CardPaymentDao = DataAccess.CardPaymentDao;
        private static readonly IAircraftSeatDao AircraftSeatDao = DataAccess.AircraftSeatDao;
        private static readonly IPaymentMethodDao PaymentMethodDao = DataAccess.PaymentMethodDao;
        private static readonly ICardTypeDao CardTypeDao = DataAccess.CardTypeDao;
        private static readonly IMemberDao MemberDao = DataAccess.MemberDao;
        private static readonly IFlightDao flightDao = DataAccess.FlightDao;
        private static readonly IFlightSeatDao FlightSeatDao = DataAccess.FlightSeatDao;
        private static readonly ICustomerDao CustomerDao = DataAccess.CustomerDao;

        #region Booking

        public List<BookingDTO> GetPaymentDueBookings(DateTime paymentDueDate, string firstName, string lastName)
        {
            return Mapper.ToDto(BookingDao.GetPaymentDueBookings(paymentDueDate, firstName, lastName));
        }

        public List<BookingDTO> GetBookingsByCustomer(int customerId)
        {
            // if payment is needed you can add ICardPaymentDao and IMemberPaymentDao before returning the booking
            return Mapper.ToDto(BookingDao.GetBookingsByCustomer(customerId));
        }

        public BookingDTO GetBooking(string bookingCode)
        {
            var booking = BookingDao.GetBooking(bookingCode);
            if (booking == null)
                return null;

            booking.Tickets = TicketDao.GetTickets(booking.BookingId);

            return Mapper.ToDto(booking);
        }

        public string CreateBooking(BookingDTO booking)
        {
            var bookingBo = Mapper.FromDto(booking);

            //if (bookingBo.Validate())
            //{
                BookingDao.CreateBooking(bookingBo);
            //}
            //else
            //{
            //    string errors = bookingBo.ValidationErrors.Aggregate(string.Empty, (current, error) => current + (error + Environment.NewLine));

            //    throw new FaultException(errors, new FaultCode("Ivalid Operation"));
            //}

            return bookingBo.BookingCode;
        }

        public void CancelBooking(int bookingId)
        {
            BookingDao.CancelBooking(bookingId);
        }

        #endregion

        #region Payment

        public void SetMemberPayment(MemberPaymentDTO memberPayment, BookingDTO booking)
        {
            // You can do other workflow checks here before Inserting, Updating etc.
            var payment = Mapper.FromDto(memberPayment);

            if (payment.Validate())
            {
                int points = MemberDao.GetMemberPoints((int) booking.CustomerId);
                
                payment.ComputePaymentPoints();

                if (payment.PaymentPoints > points)
                {
                    throw new FaultException("Member does not have enough member points to purchase this booking.", new FaultCode("Ivalid Operation"));
                }

                using (var scope = new TransactionScope())
                {
                    BookingDao.UpdateBookingStatus(booking.BookingId, "Booked");

                    
                    MemberPaymentDao.CreatePayment(payment);

                    if (booking.CustomerId != null)
                    {
                        CustomerDao.SubtractMemberPoints((int)booking.CustomerId, (int)payment.PaymentPoints);
                    }

                    scope.Complete();
                }
            }
            else
            {
                string errors = payment.ValidationErrors.Aggregate(string.Empty, (current, error) => current + (error + Environment.NewLine));

                throw new FaultException(errors, new FaultCode("Ivalid Operation"));
            }
        }

        public void SetCardPayment(CardPaymentDTO cardPayment, BookingDTO booking)
        {
            int totalRewardPoints = 0;

            using (var scope = new TransactionScope())
            {
                if (booking.CustomerId != null)
                {
                    foreach (var ticketDTO in booking.Tickets)
                    {
                        var aircraftSeat = AircraftSeatDao.GetAircraftSeatByAircraftSeatId(ticketDTO.FlightSeat.AircraftSeatId);

                        if (aircraftSeat.RewardPoint != null)
                        {
                            totalRewardPoints += (int)aircraftSeat.RewardPoint;
                        }
                    }

                    MemberDao.AddMemberPoints((int)booking.CustomerId, totalRewardPoints);
                }

                BookingDao.UpdateBookingStatus(booking.BookingId, "Booked");
                CardPaymentDao.CreatePayment(Mapper.FromDto(cardPayment));

                scope.Complete();
            }
        }

        public List<PaymentMethodDTO> GetPaymentMethods()
        {
            return Mapper.ToDto(PaymentMethodDao.GetPaymentMethods());
        }

        public List<CardTypeDTO> GetCardTypes()
        {
            return Mapper.ToDto(CardTypeDao.GetCardTypes());
        }

        #endregion

        #region Flight

        public List<AirportDTO> GetAirports()
        {
            return Mapper.ToDto(flightDao.GetAirports());
        }

        public FlightDTO GetFlightStatus(String flightCode, DateTime FlightDate, String FlightAirport, bool IsArrival)
        {
            Flight flight = flightDao.GetFlightStatus(flightCode, FlightDate, FlightAirport, IsArrival);
            if (flight != null)
                return Mapper.ToDto(flight);
            else
                return null;
        }

        public AircraftDTO getAircraftByFlightId(int flightId)
        {
            Aircraft aircraft = flightDao.GetAircraftByFlightId(flightId);

            return Mapper.ToDto(aircraft);

        }

        public void InsertFlight(FlightDTO flight)
        {
            var flg = Mapper.FromDto(flight);

            if (flg.Validate())
            {
                flightDao.InsertFlight(flg);
            }
        }

        public List<AircraftDTO> GetAircrafts()
        {
            return Mapper.ToDto(flightDao.GetAircrafts());
        }

        public List<TerminalDTO> GetTerminalByAirportId(string airportCode)
        {
            return Mapper.ToDto(flightDao.GetTerminalByAirportId(airportCode));
        }

        public List<FlightDTO> GetFlights()
        {
            return Mapper.ToDto(flightDao.GetFlights());
        }
        public FlightDTO GetFlightByFlightId(int flightId)
        {
            Flight flight = flightDao.GetFlightByFlightId(flightId);
            if (flight != null)
                return Mapper.ToDto(flight);

            return null;
        }

        public void UpdateFlight(FlightDTO flight)
        {
            var flg = Mapper.FromDto(flight);

            if (flg.Validate())
            {
                flightDao.UpdateFlight(flg);
            }
        }

        public void DeleteFlight(int flightId)
        {
            flightDao.DeleteFlight(flightId);
        }

        public List<FlightDTO> GetFlightsByFlightCode(string flightCode)
        {
            List<Flight> flights = flightDao.GetFlightsByFlightCode(flightCode);
            if (flights != null)
            {
                return Mapper.ToDto(flights);
            }

            return null;
        }

        public List<FlightDTO> GetFlights(DateTime departureDate, String departureAirport, String arrivalAirport)
        {
            return Mapper.ToDto(flightDao.GetFlights(departureDate, departureAirport, arrivalAirport));
        }

        #endregion

        #region Flight Seat

        public Boolean IsBookedSeat(int flightId, int aircraftSeatId)
        {
            return FlightSeatDao.IsBookedSeat(flightId, aircraftSeatId);
        }

        public FlightSeatDTO SaveBlockFlightSeat(FlightSeatDTO flightSeatDto)
        {
            var flightSeatBo = Mapper.FromDto(flightSeatDto);
            var resultFlightSeatDto = FlightSeatDao.SaveBlockFlightSeat(flightSeatBo);

            return Mapper.ToDto(resultFlightSeatDto);
        }
        public void CancelBlockFlightSeat(int flightSeatId)
        {
            FlightSeatDao.CancelBlockFlightSeat(flightSeatId);
        }

        #endregion
    }
}
